#!/usr/bin/env python3
# -*- coding: utf-8 -*-


from sarena import *
import minimax
import random

class SuperPlayer(Player):

    def __init__(self):
        self.nb_nodes = {}
        self.kept_state = {}

    def successors(self, state):
        board, player = state
        actions = board.get_actions() 

        # retourner que les actions intéressantes
        # - pour le début il faut retourner que des actions qu'on juge utiles (le reste on les mets ds une liste de sauvegarde)
        # - pour apres il faut retourner les actions (ordonnées) de sorte que minimax puisse utiliser le cutoff efficacement
        # - ...
        
        #actions = filter_by_
        
        # needs :
        # which player
        # current state
        
        # dico :
        # recup le dico avec hash
        # recup la clé avec score
        # pop et si c vide on va au next

        """
        current_score = self.evaluate(state)    # score of current state

        for action in actions:

            evaluated_state = (board.clone().play_action(action), -player)
            evaluated_state_action = action,evaluated_state
            evaluated_score = self.evaluate(evaluated_state)
            
            # if current state has better score than visited one, we stock it in dictionnary in the with it's score as key
            # then when we checked all better state, we return the stocked states (this should make alphabeta pruning better)
            if evaluated_score < current_score: 
                if evaluated_score not in list(self.kept_state.keys()):
                    self.kept_state[evaluated_score] = [evaluated_state_action] 
                else:
                    self.kept_state[evaluated_score].append(evaluated_state_action)
                self.successors(state)
            yield action,(board.clone().play_action(action), -player)
        for key, state_list in self.kept_state.items():
            for s in state_list:
                yield s
        """
        for action in actions:
            yield action,(board.clone().play_action(action), -player)
        
    def cutoff(self, state, depth, time_left, elapsed_time=0):
        board, player = state
        #--- Count nb of nodes traversed by depth  3._ question -----------
        if str(depth) not in self.nb_nodes.keys() :
            self.nb_nodes[str(depth)] = 1
        else:
            self.nb_nodes[str(depth)] += 1             
        

        actions = list(board.get_actions())
        # Need to cut if we are in the first steps
        if len(actions)>80 and depth>2 :
            return True
        # Need to cut if we are in the last steps
        elif time_left<10 :
            return True
        else:
            if elapsed_time>120:
                return True
            else:
                #--- Compute the elapsed time -------------------------------------
                average_nb_steps = len(actions)/8  # 8 = moyene d'actions eliminés par action 
                if(average_nb_steps>0.0):
                    average_time_by_step = time_left / average_nb_steps
                    # if average_time_by_step is elapsed then return True
                    if average_time_by_step<elapsed_time:
                        return True
        #--- sinon essayer d'aller jusqu'au bout  
        return board.is_finished()
    
    def evaluate(self, state):
        board, player = state  
        return compute_value(state)
        
    def play(self, percepts, step, time_left):
        if step % 2 == 0:
            player = -1
        else:
            player = 1
        board = Board(percepts)
        state = (board, player)
        #state.time_left = time_left
        #state.nb_actions = len(list(board.get_actions()))
        return minimax.search(state, self, time_left)
            
        
    def get_nb_nodes(self):
        return self.nb_nodes.copy()

    
def compute_value(state):
    """Return a score for this state.

    The score is the difference between the number of weighted towers of each
    player. In case of ties, it is the difference between the maximal
    height towers of each player. If self.is_finished() returns True,
    this score represents the winner (<0: red, >0: yellow, 0: draw).

    """
    board, player = state
    score = 0
    for i in range(board.rows):
        for j in range(board.columns):
            tower = board.get_tower(board.m[i][j])
            if tower:
                val_circle = 1
                if len(tower)==4:
                    val_circle = 4
                        
                if board.m[i][j][0] == 4 : # arrows
                    if tower[-1][1] == 1: # top is yello
                        score += val_circle*len(tower)
                    elif tower[-1][1] == -1: #top is red
                        score -= val_circle*len(tower)   
                else: # normal circle
                    if tower[0][0]==-1: # bottom is red
                        if tower[-1][1] == -1: # top is red
                            score -= 3*val_circle*(5-len(tower))
                        elif tower[-1][1] == 1: # top is yellow
                            score -= 2*val_circle*(5-len(tower))
                        else: 
                            score -= 1*val_circle*(5-len(tower))
                            
                    elif tower[0][0] == 1: # bottom yellow
                        if tower[-1][1] == 1: # top is yello
                            score += 3*val_circle*(5-len(tower))
                        elif tower[-1][1] == -1: #top is red
                            score += 2*val_circle*(5-len(tower))
                        else:
                            score += 1*val_circle*(5-len(tower))
                        
    if score == 0:
        
        for i in range(board.rows):
            for j in range(board.columns):
                tower = board.get_tower(board.m[i][j])
                if tower:
                    if tower[-1][1] == -1:
                        for half_tok in filter(lambda token:
                                                   (token[0] == -1 or
                                                    token[1] == -1),
                                               tower):
                            score -= 1
                    elif tower[-1][1] == 1:
                        for half_tok in filter(lambda token:
                                                   (token[0] == 1 or
                                                    token[1] == 1),
                                               tower):
                            score += 1
    return score



    
if __name__ == "__main__":
    player_main(SuperPlayer())
